﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace DiskGarden
{
	public class MyTreeView : TreeView
	{
		protected RootNode root;
		public RootNode DataSource
		{
			get { return root; }
			set
			{
				root = value;
				if (value != null)
				{
					root.NameChanged += new NodeEventHandler(Root_NameChanged);
					root.NodeAdded += new NodeEventHandler(Root_NodeAdded);
					root.NodeMoved += new NodeEventHandler(Root_NodeMoved);
					root.NodeDeleted += new NodeEventHandler(Root_NodeDeleted);
				}
			}
		}

		protected ListView listView;
		public ListView ConnectedListView
		{
			get { return listView; }
			set { listView = value; }
		}

		public new TreeNode SelectedNode
		{
			get { return base.SelectedNode; }
			set
			{
				base.SelectedNode = value;
				if (value == null)
					OnAfterSelect(new TreeViewEventArgs(null));
			}
		}

		//private bool dragDropEnabled = true;

		public MyTreeView() : base() { }

		public TreeNode FindTreeNode(Node node)
		{
			if (node == null || node.Type == NodeType.Root) return null;
			List<TreeNodeCollection> colls = new List<TreeNodeCollection>();
			colls.Add(this.Nodes);
			for (int i = 0; i < colls.Count; i++)
				foreach (TreeNode treeNode in colls[i])
				{
					if (treeNode.Tag == node)
						return treeNode;
					if (treeNode.Nodes.Count > 0)
						colls.Add(treeNode.Nodes);
				}
			return null;
		}

		#region Root Events

		void Root_NodeDeleted(Node node)
		{
			switch (node.Type)
			{
				case NodeType.Root:
					{
						this.Nodes.Clear();
						if (listView != null)
							listView.Items.Clear();
					}
					break;
				case NodeType.File:
					{
						// usuwanie odpowiedniego itemu z listView jesli tam byl
						if (listView != null)
							foreach (ListViewItem item in listView.Items)
								if (item.Tag == node)
								{
									item.Remove();
									break;
								}
					}
					break;
				case NodeType.Directory:
				case NodeType.Volume:
				case NodeType.Category:
					{
						// usuwanie z treeVIew
						TreeNode tn = FindTreeNode(node);
						if (tn != null)
							tn.Remove();
						// odswiezanie listView
						if (listView != null)
						{
							if (this.SelectedNode != null)
								(this.SelectedNode.Tag as Node).UpdateFileList(listView);
							else
								listView.Items.Clear();
						}
					}
					break;

				default:
					break;
			}


		}

		void Root_NodeAdded(Node node)
		{
			if (node.Type == NodeType.File || node.Type == NodeType.Root) return;
			TreeNode tnParent = FindTreeNode(node.Parent);
			TreeNode tnNew = new TreeNode(node.Name);
			tnNew.Tag = node;
			tnNew.ImageKey = tnNew.SelectedImageKey = node.GetImageKey();
			if (tnParent != null)
				tnParent.Nodes.Add(tnNew);
			else
				this.Nodes.Add(tnNew);
		}

		void Root_NameChanged(Node node)
		{
			if (node.Type == NodeType.File || node.Type == NodeType.Root) return;
			TreeNode tn = FindTreeNode(node);
			if (tn != null)
				tn.Text = node.Name;
		}

		void Root_NodeMoved(Node node)
		{
			if (node.Type == NodeType.File || node.Type == NodeType.Root) return;
			TreeNode tn = FindTreeNode(node);
			TreeNode tnParent = FindTreeNode(node.Parent);
			tn.Remove();
			if (tnParent != null)
				tnParent.Nodes.Add(tn);
			else
				this.Nodes.Add(tn);
		}

		#endregion

		protected override void OnAfterSelect(TreeViewEventArgs e)
		{
			base.OnAfterSelect(e);
			if (listView != null)
				if (e.Action != TreeViewAction.Collapse || e.Action != TreeViewAction.Expand)
					if (e.Node != null)
					{
						(e.Node.Tag as Node).UpdateFileList(listView);
					}
					else
					{
						listView.Items.Clear();
					}
		}

		protected override void OnAfterLabelEdit(NodeLabelEditEventArgs e)
		{
			base.OnAfterLabelEdit(e);
			if (e.CancelEdit == false && e.Label != null && e.Label.Trim() != string.Empty)
			{
				((Node)e.Node.Tag).Name = e.Label;
			}
			else
				e.CancelEdit = true;
			this.LabelEdit = false;
		}

		protected override void OnKeyUp(KeyEventArgs e)
		{
			base.OnKeyUp(e);
			if (this.SelectedNode != null && e.KeyData == Keys.F2)
			{
				this.LabelEdit = true;
				this.SelectedNode.BeginEdit();
			}
		}

		#region Drag and Drop

		protected override void OnDragEnter(DragEventArgs e)
		{
			base.OnDragEnter(e);
			if (e.Data.GetDataPresent(typeof(TreeNode)))
				e.Effect = DragDropEffects.Move;
			else
				e.Effect = DragDropEffects.None;
		}

		protected override void OnItemDrag(ItemDragEventArgs e)
		{
			base.OnItemDrag(e);
			if (e.Button == MouseButtons.Left)
			{
				this.SelectedNode = e.Item as TreeNode;
				Node node = this.SelectedNode.Tag as Node;
				if (node != null)
				{
					if (node.Type == NodeType.Category || node.Type == NodeType.Volume)
						DoDragDrop(e.Item, DragDropEffects.Move);
				}
			}
		}

		private TreeNode dragdrop_prevNode = null;
		protected override void OnDragOver(DragEventArgs e)
		{
			base.OnDragOver(e);
			TreeNode destTreeNode = FindTreeNode(e.X, e.Y);
			// jesli najedziemy na jakis treeNode
			if (destTreeNode != null)
			{
				// jesli docelowy node nie jest kategorią (lub rootem)
				if (destTreeNode.Tag is CategoryNode == false)
				{
					e.Effect = DragDropEffects.None;
				}
				else
				{
					e.Effect = DragDropEffects.Move;
					TreeNode srcTreeNode = e.Data.GetData(typeof(TreeNode)) as TreeNode;
					// jesli przypadkiem 'dragowany' obiekt nie jest TreeNodem
					if (srcTreeNode == null)
					{
						e.Effect = DragDropEffects.None;
					}
					else
					{
						TreeNode temp = destTreeNode;
						// jesli docelowy node byłby sub-nodem zrodlowego node'a
						while ((temp = temp.Parent) != null) if (temp == srcTreeNode)
							{
								e.Effect = DragDropEffects.None;
								break;
							}
					}
					// kolorki
					if (destTreeNode != dragdrop_prevNode)
					{
						if (dragdrop_prevNode != null)
							dragdrop_prevNode.BackColor = Color.Empty;
						destTreeNode.BackColor = SystemColors.Highlight;
						dragdrop_prevNode = destTreeNode;
					}
				}
			}
			else
			{
				e.Effect = DragDropEffects.Move;
				if (dragdrop_prevNode != null)
				{
					dragdrop_prevNode.BackColor = Color.Empty;
					dragdrop_prevNode = null;
				}
			}
		}

		protected override void OnDragDrop(DragEventArgs e)
		{
			base.OnDragDrop(e);
			if (dragdrop_prevNode != null)
				dragdrop_prevNode.BackColor = Color.Empty;
			TreeNode srcTreeNode = e.Data.GetData(typeof(TreeNode)) as TreeNode;
			TreeNode destTreeNode = FindTreeNode(e.X, e.Y);
			if (srcTreeNode != null && srcTreeNode != destTreeNode)
			{
				Node nSrc = (Node)srcTreeNode.Tag;
				CategoryNode nDest;
				if (destTreeNode != null)
				{
					// sprawdzenie czy nie przenosimy noda to swojego sub-noda
					TreeNode temp = destTreeNode;
					while ((temp = temp.Parent) != null) if (temp == srcTreeNode) return;

					nDest = destTreeNode.Tag as CategoryNode;
					if (nDest == null) return;
				}
				else
				{
					nDest = root;
				}

				nSrc.Move(nDest);
				srcTreeNode.EnsureVisible();
				this.SelectedNode = srcTreeNode;
			}
		}

		protected override void OnDragLeave(EventArgs e)
		{
			base.OnDragLeave(e);
			if (dragdrop_prevNode != null)
				dragdrop_prevNode.BackColor = Color.Empty;
		}

		private TreeNode FindTreeNode(int x, int y)
		{
			Point pt = new Point(x, y);
			pt = this.PointToClient(pt);
			TreeViewHitTestInfo hit = this.HitTest(pt);
			return hit.Node;
		}
		#endregion


	}


}

